import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.*;
import java.net.Socket;
import java.util.Collections;
import java.util.Map;


public class GameFrame extends JFrame {
    //初始化窗口大小，游戏对象单位的大小（一个正方形  节点的边长）
    //初始化蛇、食物、围墙、GameComponent、GameRunnable,并运行GameRunnable
    //食物和围墙都是蛇的成员
    //setKeyListener 监听键盘 改变蛇的运动方向
    //若游戏结束标志位置1，则弹出游戏结束信息，并结束游戏
    public GameFrame() throws IOException, ClassNotFoundException {
        //nickname 是 final 类型，必须放在构造函数里面初始化
        nickName = JOptionPane.showInputDialog("请输入昵称：");
        initViriables();
        gc = new GameComponent(snake);
        int interval = Integer.valueOf(JOptionPane.showInputDialog("请输入难度(1-5)："));
        //interval最好在50-250之间 5级难度
        GameRunnable r = new GameRunnable(gc, snake, nickName, socket, 300 - 50 * interval);
        setKeyListener();
        initSize();
        add(gc, BorderLayout.CENTER);
        Thread t = new Thread(r);
        t.start();
    }

    public void initViriables() throws IOException {
        snake = new Snake();
        String ip = JOptionPane.showInputDialog("请输入服务器的ip地址：");
        socket = new Socket(ip, 12345);//连接服务器
    }

    public void initSize() {
        setVisible(true);
        setTitle("贪吃蛇");
        setSize(PanelSize.XLength * PanelSize.cell, PanelSize.YLength * PanelSize.cell);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    private void setKeyListener() {
        addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                switch (e.getKeyCode()) {
                    case KeyEvent.VK_UP:
                        if (snake.getD() != Directions.DOWN)
                            snake.setDirection(Directions.UP);
                        break;
                    case KeyEvent.VK_DOWN:
                        if (snake.getD() != Directions.UP)
                            snake.setDirection(Directions.DOWN);
                        break;
                    case KeyEvent.VK_LEFT:
                        if (snake.getD() != Directions.RIGHT)
                            snake.setDirection(Directions.LEFT);
                        break;
                    case KeyEvent.VK_RIGHT:
                        if (snake.getD() != Directions.LEFT)
                            snake.setDirection(Directions.RIGHT);
                        break;
                    default:
                        break;
                }
            }
        });
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        new GameFrame();
    }

    private GameComponent gc;
    private Snake snake;
    private final String nickName;//昵称，用于map,final类型，一旦确定就不准修改
    private Socket socket;

}

class GameComponent extends JPanel {
    public GameComponent(Snake snake) {
        this.snake = snake;
    }

    //setNameSnakeMap方法 为了把对手的图形绘制出来，不得不这样做
    public void setNameSnakeMap(Map<String, Snake> scoresMap) {
        this.nameSnakeMap = scoresMap;
    }

    //为了绘制地图
    public void setGameMap(GameMap gameMap) {
        this.gameMap = gameMap;
    }
    //绘制图形，不需要主动调用，repaint()方法会自动调用它

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        //画墙
        for (Node n : gameMap.getWall())
            g.fillRect(n.getX() * PanelSize.cell, n.getY() * PanelSize.cell, PanelSize.cell, PanelSize.cell);

        //画食物
        Food food = gameMap.getFood();
        g.setColor(Color.red);
        g.fillOval(food.getX() * PanelSize.cell, food.getY() * PanelSize.cell, PanelSize.cell, PanelSize.cell);

        g.setColor(Color.black);
        //画蛇
        for (Map.Entry entry : nameSnakeMap.entrySet()) {
            Snake aSnake = (Snake) entry.getValue();
            for (Node n : aSnake.getBody()) {
                g.fillRect(n.getX() * PanelSize.cell, n.getY() * PanelSize.cell, PanelSize.cell, PanelSize.cell);
            }
        }


        //画分数

        g.setColor(Color.BLUE);
        g.setFont(new Font("微软雅黑", Font.BOLD, 20));

        int count = 1;
        for (Map.Entry entry : nameSnakeMap.entrySet()) {
            Snake aSnake = (Snake) entry.getValue();
            System.out.println("画分数" + count + "次:" + entry.getKey() + ":" + entry.getValue());
            g.drawString(entry.getKey() + ":" + aSnake.getBodyLength(), PanelSize.cell * 8 * count, PanelSize.cell * 3);
            count++;
        }


    }

    private GameMap gameMap;
    private Snake snake;

    private Map<String, Snake> nameSnakeMap;
}

class GameRunnable implements Runnable {
    public GameRunnable(GameComponent gc, Snake snake, String nickName, Socket socket, int interval) throws IOException, ClassNotFoundException {
        this.gc = gc;
        this.snake = snake;
        this.interval = interval;
        this.nickname = nickName;

        //获取输入输出流对象 前提是对象的类必须实现 java.io.Serializable 接口
        os = socket.getOutputStream();
        is = socket.getInputStream();
        oos = new ObjectOutputStream(os);
        ois = new ObjectInputStream(is);
        firstConnect();
    }

    //第一次连接  发昵称，接受地图
    private void firstConnect() throws IOException, ClassNotFoundException {
        oos.reset();
        oos.writeObject(nickname);
        oos.flush();

        gameMap = (GameMap) ois.readObject();
    }


    @Override
    public void run() {

        try {
//                       while (snake.isLiving()) {
            while (true) {

                //move
                snake.move(gameMap);
                sendMsg();
                getMsg();
                // 调用GameComponent的paintComponent方法，绘制图像
                gc.setNameSnakeMap(nameSnakeMap);
                gc.setGameMap(gameMap);

                gc.repaint();

                //判断死亡
                if (!snake.isLiving()) {

                    String[] choices = {"是", "否"};
                    int playAgain = JOptionPane.showOptionDialog(gc, "玩家" + nickname + "已死亡！\n" + "是否要继续游戏：", "继续游戏", JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, choices, choices[0]);
                    System.out.println(playAgain);
                    if (playAgain == 0) {
                        snake.revive();
                        continue;
                    }
                    System.exit(0);
                    break;
                }
                //sleep
                Thread.sleep(interval);

            }

        } catch (InterruptedException | IOException | ClassNotFoundException interruptedException) {
            interruptedException.printStackTrace();
        }
        Thread.currentThread().interrupt();
        System.out.println("Thread was interrupted");

    }

    //只发自己的蛇
    //发自己是否吃食物了
    public void sendMsg() throws IOException {
        DataOutputStream dos = new DataOutputStream(os);
        dos.writeBoolean(snake.isEating());//先发送是否吃食物
        dos.flush();
        oos.reset();
        oos.writeObject(snake);
        oos.flush();
    }

    //返回接受到的 地图 和 昵称-蛇 键值对表
    public void getMsg() throws IOException, ClassNotFoundException {
        gameMap = (GameMap) ois.readObject();//先返回地图
        nameSnakeMap = (Map<String, Snake>) ois.readObject();
    }

    GameComponent gc;
    Snake snake;
    GameMap gameMap;
    final String nickname;
    Map<String, Snake> nameSnakeMap;
    OutputStream os;
    InputStream is;
    ObjectInputStream ois;
    ObjectOutputStream oos;
    int interval;//调节屏幕刷新速度,interval值越大，两次刷新间隔时间越长，刷新速度越慢
}

